// Copyright (C) 2005 Taylor L. Riché <riche@cs.utexas.edu>
//  
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software 
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// 	$Id: lagniappe.g 78 2005-08-02 16:28:11Z richetl $	
//

{
    import antlr.StringUtils;
}

class LltLexer extends Lexer;

options {
    k=2; // needed for newline junk
    charVocabulary='\u0000'..'\u007F'; // allow ascii
    testLiterals = false;
}

tokens {
	PPA="ppa";
	PPF="ppf";
    WIR="wiring";
    IMP="import";
    EXIT="exits";
    FEAT="features";
    EM="emethods";
    ED="edata";
    EINC="eincs";
    SYS="sys";
    INC="inc";
    ARG="arg"; PTRTYP="ptrtyp"; REFTYP="reftyp";
    TYP="typ"; GENTYP="gentyp"; DECL="decl";
    NSTYP="nstyp"; INC_C="inc_c"; INC_T="inc_t";
}

// an identifier.  Note that testLiterals is set to true!  This means
// that after we match the rule, we look in the literals table to see
// if it's a literal or really an identifer
IDENT
	options {testLiterals=true;}
	:	('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|'$')*
	;

// character literals
CHAR_LITERAL
	:	'\'' ( ESC | ~('\''|'\n'|'\r'|'\\') ) '\''
	;

// string literals
STRING_LITERAL
	:	'"' (ESC|~('"'|'\\'|'\n'|'\r'))* '"'
	; 

LBRACE: '{';
RBRACE: '}';
LPAREN: '(' ;
RPAREN: ')' ;
INT   : ('0'..'9')+ ;
DELIM : ';';
COMMA : ',';
RA	  : "->";
DOT	  : '.';
ASSIGN: '=';
NOT_EQUAL: "!=";
EQUAL: "==";
RBRACK: "]";
LBRACK: "[";
NS: "::";
STAR: '*';
AMP: '&';
LANGLE: '<';
RANGLE: '>';
WS: 
    ( ' '
    | '\t'
    | ( "\r\n" | '\r' | '\n' ) 
      { newline (); }
    )
    { $setType(Token.SKIP); }
;    

protected
ESC
	:	'\\'
		(	'n'
		|	'r'
		|	't'
		|	'b'
		|	'f'
        |   'w'
        |   'a'
		|	'"'
		|	'\''
		|	'\\'
		|	('u')+ HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
		|	'0'..'3'
			(
				options {
					warnWhenFollowAmbig = false;
				}
			:	'0'..'7'
				(
					options {
						warnWhenFollowAmbig = false;
					}
				:	'0'..'7'
				)?
			)?
		|	'4'..'7'
			(
				options {
					warnWhenFollowAmbig = false;
				}
			:	'0'..'7'
			)?
		)
	;

// hexadecimal digit (again, note it's protected!)
protected
HEX_DIGIT
	:	('0'..'9'|'A'..'F'|'a'..'f')
	;

/*
 * PARSER
 *
 */

class LltParser extends Parser;

options {
	importVocab=LltLexer;
	buildAST = true;
    k=2;
}

compilation_unit
    : ppf_block
    | (import_block)* ppa_block
    ;

import_block
    : IMP^ IDENT DELIM!
    ;

ppa_block
    : PPA^ IDENT LBRACE! (ppf_decl_block)? wiring_block RBRACE!
    ;

ppf_decl_block
    : DECL^ LBRACE! (ppf_decl)+ RBRACE!
    ;

ppf_decl
    : IDENT IDENT^ DELIM!
    ;

wiring_block
    : WIR^ LBRACE! (wiring)+ RBRACE!
    ;

wiring
    : port RA^ IDENT DELIM!
    ;

port
    : IDENT DOT^ IDENT
    ;

ppf_block
    : PPF^ IDENT LBRACE! exit_block (features_block)? (extra_incs)? 
        (extra_data)? (extra_methods)? RBRACE!
    ;

exit_block
    : EXIT^ (exit)+ 
    ;

exit
    : IDENT exit
    | COMMA! IDENT exit
    | DELIM!
    ;

features_block
    : FEAT^ features_list
    ;

features_list
    : feature features_list
    | COMMA! feature features_list
    | DELIM!
    ;

feature
    : IDENT^ (LBRACK! IDENT RBRACK!)?
    ;

extra_methods
    : EM^ LBRACE! (cpp_method)+ RBRACE!
    ;

cpp_method
    : type IDENT^ LPAREN! (cpp_method_arg)* RPAREN! DELIM!
    ;

cpp_method_arg!
    : i:type j:IDENT
        {#cpp_method_arg = #([ARG, "arg"], #j, #i);}
    ;

extra_data
    : ED^ LBRACE! (cpp_data)+ RBRACE!
    ;

cpp_data!
    : i:type j:IDENT DELIM!
        {#cpp_data = #([ARG, "arg"], #j, #i);}
    ;

type!
    : i:IDENT
        {#type = #([TYP, "typ"], #i);}
    | ns:IDENT NS ty:type
        {#type = #([NSTYP, "nstyp"], #ns, #ty);}
    | ii:IDENT LANGLE t:type RANGLE
        {#type = #([GENTYP, "gentyp"], #ii, #t);}
    | STAR tt:type 
        {#type = #([PTRTYP, "ptrtyp"], #tt);}
    | AMP ttt:type
        {#type = #([REFTYP, "reftyp"], #ttt);}
    ;

extra_incs
    : EINC^ LBRACE! (cpp_inc)+ RBRACE!
    ;

cpp_inc!
    : (dot_ident DELIM)=>i:dot_ident DELIM!
        {#cpp_inc = #([INC, "inc"], #i);}
    | c:cpp_inc_chain
        {#cpp_inc = #c;}
    | s:SYS j:cpp_inc_chain
        {#cpp_inc = #(#s, #j);}
    ;

cpp_inc_chain!
    : i:dot_ident DELIM!
        {#cpp_inc_chain = #([INC_T, "inc_t"], #i);}
    | ii:IDENT c:cpp_inc_chain
        {#cpp_inc_chain = #([INC_C, "inc_c"], #ii, #c);}
    ;

dot_ident
    : ii:IDENT
    | i:IDENT^ DOT! j:IDENT!
        {#i.setText(i.getText() + "." + j.getText());}
    ;

/*
 * TREE PARSER
 *
 */

{
    import java.io.*;
    import java.util.*;
}

class LltTreeParser extends TreeParser;

options {
    importVocab = LltParser;
    buildAST = true;
}

compilation_unit
{
    LinkedList importTypes = new LinkedList();
}
    : ppf_block
    | (import_block[importTypes])* ppa_block[importTypes]
    ;

import_block [LinkedList importTypes]
    : #(IMP i:IDENT)
        {
            try {
                String filename = i.getText() + ".lp";
                BufferedReader in = null;
                try {
                    in = new BufferedReader(new FileReader(filename));
                }
                catch (FileNotFoundException e) {
                    System.err.println("\nERROR: Imported file \"" + filename + 
                                       "\" does not exist.");
                    System.err.println();
                    System.exit(1);
                }
                LltLexer lexer = new LltLexer(in);
                System.out.println("Translating file: " + filename);

                // Create parser object and parse the input

                LltParser parser = new LltParser(lexer);
                parser.compilation_unit();

                // Get the AST generated by the parser and
                // start translation

                AST t = parser.getAST();
                LltTreeParser translator = new LltTreeParser();
                translator.compilation_unit(t);
                // get the imports for checking in the ppa
                importTypes.addLast(i.getText());
            }
            catch (Exception e) {
                System.err.println("DEATH:" + e.getMessage());
                e.printStackTrace();
                System.exit(1);
            }
        }
    ;

ppa_block [LinkedList importTypes]
{
    PpaDecls d = null;
    PpaWirings w = null;
}
    : #(PPA name:IDENT (d = ppf_decl_block)? w = wiring_block )
        {
            AGFactoryPrinter printer = new AGFactoryPrinter();
            try {
                d.validate(importTypes);
            }
            catch (PpaDecls.NotImportedException e) {
                System.err.println("\nERROR: PPF type \"" + e.getBadType() + 
                                   "\" was not imported.");
                System.err.println();
                System.exit(1);
            }

            printer.print(name.getText(), d, w);
        }
    ;

ppf_decl_block returns [PpaDecls d]
{
    d = new PpaDecls();
}
    : #(DECL (ppf_decl[d])+)
    ;

ppf_decl [PpaDecls d]
    : #(name:IDENT type:IDENT)
        {
            PpaDecls.Decl decl = new PpaDecls.Decl(name.getText(), 
                                                   type.getText());
            d.addDecl(decl);
        }
    ;

wiring_block returns [PpaWirings w]
{
    w = new PpaWirings();
}
    : #(WIR (wiring[w])+)
    ;

wiring [PpaWirings w]
    : #(RA #(DOT src:IDENT exit:IDENT) dest:IDENT)
        {
            PpaWirings.Wiring wiring = new PpaWirings.Wiring();
            wiring.setSrcName(src.getText());
            wiring.setSrcExit(exit.getText());
            wiring.setDest(dest.getText());
            w.addWiring(wiring);
        }
    ;

ppf_block
{
    PpfExits e = null;
    PpfFeatures f = null;
    CppIncludes i = null;
    CppData d = null;
    CppMethods  m = null;
}
    : #(PPF name:IDENT e = exit_block (f = features_block)? (i = extra_incs)? 
            (d = extra_data)? (m = extra_methods)?)
        {
            PpfHeaderPrinter printer = new PpfHeaderPrinter();
            printer.print(name.getText(), e, f, i, d, m);
        }
    ;

exit_block returns [PpfExits e]
{
    e = new PpfExits();
}
    : #(EXIT (exit[e])+)
    ;

exit [PpfExits e]
    : i:IDENT
        {
            PpfExits.Exit exit = new PpfExits.Exit();
            exit.setName(i.getText());
            e.addExit(exit);
        }
    ;

features_block returns [PpfFeatures f]
{
    f = new PpfFeatures();
}
    : #(FEAT (feature[f])+)
    ;

feature [PpfFeatures f]
    : #(i:IDENT (j:IDENT)?)
        {
            if(i.getFirstChild() != null) {
                f.setFeature(i.getText(), j.getText());
            }
            else {
                f.setFeature(i.getText());
            }
        }
    ;

extra_methods returns [CppMethods m]
{
    m = new CppMethods();
}
    : #(EM (cpp_method[m])+)
    ;

cpp_method [CppMethods m]
{
    String t;
    LinkedList l = new LinkedList();
}
    : #(i:IDENT t = type (cpp_method_arg[l])*)
        {
            CppMethods.Method method = new CppMethods.Method();
            method.setName(i.getText());
            method.setType(t);
            method.setArgList(l);
            m.addMethod(method);
        }
    ;

cpp_method_arg [LinkedList l]
{
    String t;
}
    : #(ARG i:IDENT t = type)
        {
            CppMethods.Arg a = new CppMethods.Arg();
            a.setName(i.getText());
            a.setType(t);
            l.addLast(a);
        }
    ;

extra_data returns [CppData c]
{
    c = new CppData();
}
    : #(ED (cpp_data[c])+ )
    ;

cpp_data [CppData c]
{
    CppData.DataItem item = new CppData.DataItem();
    String t;
}
    : #(ARG i:IDENT t = type)
        {
            item.setName(i.getText());
            item.setType(t);
            c.addData(item);
        }
    ;

type returns [String t]
{
    String g, p, r;
    t = null;
}
    : #(TYP i:IDENT)
        {t = i.getText();}
    | #(NSTYP ns:IDENT r = type)
        {t = ns.getText() + "::" + r;}
    | #(GENTYP ii:IDENT g = type)
        {t = ii.getText() + "< " + g + " >";}
    | #(PTRTYP p = type)
        {t = p + " *";}
    | #(REFTYP r = type)
        {t = r + " &";}
    ;

extra_incs returns [CppIncludes i]
{
    i = new CppIncludes();
}
    : #(EINC (cpp_inc[i])+ )
    ;

cpp_inc [CppIncludes i]
{
    CppIncludes.Include inc = new CppIncludes.Include();
    String s,iii;
}
    : #(INC ii:IDENT)
        {
            inc.setName(ii.getText());
            i.addInclude(inc);
        }
    | s = cpp_inc_chain
        {
            inc.setName(s);
            i.addInclude(inc);
        }
    | #(SYS iii = cpp_inc_chain)
        {
            inc.setName(iii);
            inc.setSys(true);
            i.addInclude(inc);
        }
    ;

cpp_inc_chain returns [String s]
{
    String r;
    s = "";
}
    : #(INC_C i:IDENT r = cpp_inc_chain)
        {s = i.getText()+ "/" + r;}
    | #(INC_T ii:IDENT)
        {s = ii.getText();}
    ;
